package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class DeviceServiceImpl implements DeviceService {
    private static final Logger log = LoggerFactory.getLogger(DeviceServiceImpl.class);
    @Autowired
    private Client client;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;


    /**
     * 从互联网平台同步产品列表
     */
    @Override
    public void syncProductList() {
        // 1、创建请求对象实例
        QueryProductListRequest request = new QueryProductListRequest();
        request.setCurrentPage(1);
        request.setPageSize(200);
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //2、 响应对象实例
        QueryProductListResponse response=null;
        try {
             response = client.queryProductList(request);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        //3、判断查看所有产品列表是否成功
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        //4、得到响应对象里的信息
        List<QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo> productInfo = response.getBody().getData().getList().productInfo;
        List<ProductVo> list = productInfo.stream().map(prodect -> {
            ProductVo bean = BeanUtil.toBean(prodect, ProductVo.class);
            return bean;
        }).collect(Collectors.toList());
        //5、保存到Redis里面
        redisTemplate.opsForValue().set(CacheConstants.IOT_ALL_PRODUCT,JSONUtil.toJsonStr(list));
    }

    /**
     * 查询所有产品列表
     * 产品 produce
     */
    @Override
    public List<ProductVo> allProduct() {
        // 1、从Redis中获取产品列表数据
        String s = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT);
        // 2、判断获取到的产品列表是否为空
        if(StrUtil.isEmpty(s)){
            return Collections.emptyList();
        }
        // 3、数据转换封装
        List<ProductVo> list = JSONUtil.toList(s, ProductVo.class);
        return list;
    }

    /**
     * 注册设备
     */
    @Override
    public void RegisterDevice(DeviceDto deviceDto) {
        //1. 判断设备名称是否重复
        Integer queryDevice = deviceMapper.queryDeviceName(deviceDto.getDeviceName());
        if(queryDevice > 0){
            throw new BaseException(BasicEnum.LOCATION_BINDING_PRODUCT);
        }
        //1. 保存设备到IOT平台
        RegisterDeviceRequest request = deviceDto.getRegisterDeviceRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse response;
        try {
            response = client.registerDevice(request);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        if(!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //3. 保存设备信息到本地数据库
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        // 获取阿里云物联网平台iotId
        device.setIotId(response.getBody().data.getIotId());
        try {
            // 物理设备地址
            if(Objects.equals(deviceDto.getLocationType(), DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
                device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
            }
            // 产品名称数据
            // 调用物联网平台查询产品信息
            // 创建请求对象实例
            QueryProductRequest queryProductRequest = new QueryProductRequest();
            // 公共实例
            queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            queryProductRequest.setProductKey(device.getProductKey());
            // 查询公共实例下的所有产品
            QueryProductResponse queryProductResponse=null;
            try {
                queryProductResponse = client.queryProduct(queryProductRequest);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
            }
            if(!queryProductResponse.getBody().success){
                log.error("查询物联网产品信息失败，失败原因：{}",queryProductResponse.getBody());
            }
            String productName = queryProductResponse.getBody().getData().getProductName();
            device.setProductName(productName);
            // 是否包含门禁
            device.setHaveEntranceGuard(productName.contains("门禁")?1:0);
            // 新增设备到本地数据库
            deviceMapper.insert(device);
        } catch (BaseException e) {
            e.printStackTrace();
            // 删除物联网平台中的设备
            // 创建请求对象实例
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setIotId(device.getIotId());
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            // 响应对象实例
            DeleteDeviceResponse deleteDeviceResponse=null;
            try {
                 deleteDeviceResponse = client.deleteDevice(deleteDeviceRequest);
            } catch (Exception ex) {
                e.printStackTrace();
                throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
            }
            if(!deleteDeviceResponse.getBody().success){
                log.error("删除物联网平台设备出错，错误信息：{}",deleteDeviceResponse.getBody());
                throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
            }
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
    }

    /**
     * 分页查询设备列表
     */
    @Override
    public PageResponse<DeviceVo> pageQuery(DeviceDto deviceDto) {
        //1. 开启分页
        PageHelper.startPage(deviceDto.getPageNum(),deviceDto.getPageSize());
        //2. 查询数据列表
        Page<DeviceVo> page =deviceMapper.pageQuery(deviceDto);
        return  PageResponse.of(page,DeviceVo.class);
    }

    /**
     * 删除设备
     */
    @Override
    public void deleteDevice(DeviceDto deviceDto) {
        if(deviceDto==null){
            throw new BaseException(BasicEnum.PARAMETER_MISSING);
        }
        // 1、删除物联网平台中的设备
        // 创建请求对象实例
        DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
        deleteDeviceRequest.setIotId(deviceDto.getIotId());
        deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        // 响应对象实例
        DeleteDeviceResponse deleteDeviceResponse=null;
        try {
             deleteDeviceResponse = client.deleteDevice(deleteDeviceRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        // 判断物联网平台是否操作成功
        if(!deleteDeviceResponse.getBody().success){
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        // 2、删除数据库的设备信息
        deviceMapper.deleteDevice(deviceDto.getIotId(),deviceDto.getProductKey());
    }

    /**
     * 查询设备详细数据
     */
    @Override
    public DeviceVo QueryDeviceDetail(DeviceDto deviceDto) {
        //参数校验
        if(StrUtil.isEmpty(deviceDto.getIotId())){
            throw new BaseException(BasicEnum.PARAMETER_MISSING);
        }
        //1. 调用物联网平台查询设备详情数据
        // 创建请求对象实例
        QueryDeviceDetailRequest queryDeviceDetailRequest = new QueryDeviceDetailRequest();
        // 如果传入IotId参数，则无需传入ProductKey和DeviceName参数
        queryDeviceDetailRequest.setIotId(deviceDto.getIotId());
        // 实例ID
        queryDeviceDetailRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDeviceDetailResponse queryDeviceDetailResponse=null;
        try {
            queryDeviceDetailResponse = client.queryDeviceDetail(queryDeviceDetailRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        if(!queryDeviceDetailResponse.getBody().success){
            log.error("查询物联网设备出错,错误原因:{}", queryDeviceDetailResponse.getBody());
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = queryDeviceDetailResponse.getBody().getData();

        // 2. 调用数据访问层查询设备详情数据
        Device device = deviceMapper.QueryDeviceDetail(deviceDto);
        if(device == null){
            throw new BaseException(BasicEnum.DATA_NOT_EXITS);
        }

        //3. 将物联网平台返回的数据和数据访问层返回的数据合并到一起返回给客户端
        // 3.1 将数据访问层返回的数据封装到DeviceVo中
        DeviceVo deviceVo = BeanUtil.toBean(device, DeviceVo.class);
        // 3.2 再将物联网平台返回的数据封装到DeviceVo中
        BeanUtil.copyProperties(data,deviceVo, CopyOptions.create().ignoreNullValue());
        return deviceVo;
    }

    /**
     * 修改设备备注名称
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        log.error("修改信息详情：{}",JSONUtil.toJsonStr(deviceDto));
        //1、调用物联网平台修改设备备注名称
        //创建请求对象实例
        BatchUpdateDeviceNicknameRequest batchUpdateDeviceNicknameRequest = new BatchUpdateDeviceNicknameRequest();
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo info = new BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo();
        // 如果传入IotId参数，则无需传入ProductKey和DeviceName参数
        info.setIotId(deviceDto.getIotId());
        info.setNickname(deviceDto.getNickname());
        batchUpdateDeviceNicknameRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        batchUpdateDeviceNicknameRequest.setDeviceNicknameInfo(List.of(info));
        // 响应对象实例
        BatchUpdateDeviceNicknameResponse response;
        try {
             response = client.batchUpdateDeviceNickname(batchUpdateDeviceNicknameRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        // 判断物联网平台是否操作成功
        if(!response.getBody().success){
            log.error("修改物联网设备出错,错误码:{}+错误信息:{}",response.getBody().code,response.getBody().getErrorMessage());
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //2、修改数据库设备备注名称
        deviceMapper.updateDevice(deviceDto);
    }

    /**
     * 查询物模型功能定义详情
     */
    @Override
    public Map<String, String> queryThingModelPublished(DeviceDto deviceDto) {
        if (StrUtil.isEmpty(deviceDto.getProductKey())) {
            throw new BaseException(BasicEnum.PARAMETER_MISSING);
        }
        //调用物联网平台查询物模型定义
        QueryThingModelRequest queryThingModelRequest = new QueryThingModelRequest();
        queryThingModelRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        queryThingModelRequest.setProductKey(deviceDto.getProductKey());

        QueryThingModelResponse queryThingModelResponse = null;
        try {
            queryThingModelResponse = client.queryThingModel(queryThingModelRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        if (!queryThingModelResponse.getBody().success) {
            log.info("查询物模型定义信息错误,错误原因:{}", queryThingModelResponse.getBody());
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        String thingModelJson = queryThingModelResponse.getBody().getData().getThingModelJson();

        return Map.of("thingModelJson", thingModelJson);
    }

    /**
     * 查询指定设备的物模型运行状态
     */
    @Override
    public Map<String, Object> queryDevicePropertyStatus(DeviceDto deviceDto) {
        if (StrUtil.isEmpty(deviceDto.getProductKey()) || StrUtil.isEmpty(deviceDto.getDeviceName())) {
            throw new BaseException(BasicEnum.PARAMETER_MISSING);
        }
        QueryDevicePropertyStatusRequest queryDevicePropertyStatusRequest = new QueryDevicePropertyStatusRequest();
        queryDevicePropertyStatusRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        queryDevicePropertyStatusRequest.setProductKey(deviceDto.getProductKey());
        queryDevicePropertyStatusRequest.setDeviceName(deviceDto.getDeviceName());

        QueryDevicePropertyStatusResponse queryDevicePropertyStatusResponse = null;
        try {
            queryDevicePropertyStatusResponse = client.queryDevicePropertyStatus(queryDevicePropertyStatusRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }

        if (!queryDevicePropertyStatusResponse.getBody().success) {
            log.error("查询物模型运行状态调用失败,失败原因:{}",queryDevicePropertyStatusResponse.getBody());
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyDataList dataList = queryDevicePropertyStatusResponse.getBody().getData().getList();
        return Map.of("list", dataList);
    }

    /**
     * 按天统计查询指标数据
     */
    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {
       // 数据格式处理
        LocalDateTime start = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);
        // 1、初始化当前24小时的数据（24小时，数据值为0）
        List<DeviceDataGraphVo> deviceDataGraphVos = DeviceDataGraphVo.dayInstance(start);
        // 2、调用数据访问层查询统计数据
        List<DeviceDataGraphVo> dbList =deviceMapper.queryDeviceDataListByDay(iotId,functionId,start,end);
        // 将数据库查询的数据转化为Map集合，方便后期通过key(小时)找value（数据）
        if (CollectionUtil.isEmpty(dbList)) {
            return deviceDataGraphVos;
        }
        Map<String, Double> dbMap = dbList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));
        // 3、将数据库查询到的数据，设置到初始数据集合中
        deviceDataGraphVos.forEach(deviceDataGraphVo -> {
            Double value = dbMap.getOrDefault(deviceDataGraphVo.getDateTime(), 0.0d);
            deviceDataGraphVo.setDataValue(value);
        });
        return deviceDataGraphVos;
    }

    /**
     * 按周统计查询指标数据
     */
    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {
        // 数据格式处理
        LocalDateTime start = LocalDateTimeUtil.of(startTime);
        LocalDateTime end = LocalDateTimeUtil.of(endTime);
        // 1、初始化当天的数据（七天，数据值为0）
        List<DeviceDataGraphVo> deviceDataGraphVos = DeviceDataGraphVo.weekInstance(start);
        // 2、调用数据访问层查询统计数据
        List<DeviceDataGraphVo> dbList =deviceMapper.queryDeviceDataListByWeek(iotId,functionId,start,end);
        // 将数据库查询的数据转化为Map集合，方便后期通过key(天)找value（数据）
        if (CollectionUtil.isEmpty(dbList)) {
            return deviceDataGraphVos;
        }
        Map<String, Double> dbMap = dbList.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));
        // 3、将数据库查询到的数据，设置到初始数据集合中
        deviceDataGraphVos.forEach(deviceDataGraphVo -> {
            Double value = dbMap.getOrDefault(deviceDataGraphVo.getDateTime(), 0.0d);
            deviceDataGraphVo.setDataValue(value);
        });
        return deviceDataGraphVos;
    }
}
